/**
 * This file is Copyright © 2008 Software Craftsmen Limited. All Rights Reserved.
 */
package com.softwarecraftsmen.cachecontrol;

import com.softwarecraftsmen.functors.NullaryFunctor;
import com.softwarecraftsmen.functors.NullaryReturningFunctor;
import com.softwarecraftsmen.functors.UnaryFunctor;
import com.softwarecraftsmen.functors.UnaryReturningFunctor;
import com.softwarecraftsmen.cachecontrol.keys.Key;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

public class CacheItem<V> implements Item<V>
{
	@NotNull private final Cache<V> cache;
	@NotNull private final Key<V> key;
	@Nullable private final V value;

	public CacheItem(@NotNull final Cache<V> cache, @NotNull final Key<V> key, @Nullable final V value)
	{
		this.cache = cache;
		this.key = key;
		this.value = value;
	}

	public static @NotNull <V> CacheItem<V> createEmptyCacheItem(final Cache<V> cache, final Key<V> key)
	{
		return new CacheItem<V>(cache, key, null);
	}

	public final boolean isEmpty()
	{
		return value == null;
	}

	public boolean hasValue()
	{
		return !isEmpty();
	}

	public @Nullable V value()
	{
		return value;
	}

	public @NotNull Item<V> replace(@NotNull final V value)
	{
		return cache.store(key, value);
	}

	public void isEmpty(@NotNull final NullaryFunctor closure)
	{
		if (isEmpty())
		{
			closure.closure();
		}
	}

	public void hasValue(@NotNull final UnaryFunctor<V> closure)
	{
		if (hasValue())
		{
			assert value != null;
			closure.closure(value);
		}
	}

	public void ternary(@NotNull final UnaryFunctor<V> trueClosure, @NotNull final NullaryFunctor falseClosure)
	{
		hasValue(trueClosure);
		isEmpty(falseClosure);
	}

	@NotNull public <T> T ternary(@NotNull final UnaryReturningFunctor<V, T> trueClosure, @NotNull final NullaryReturningFunctor<T> falseClosure)
	{
		if (hasValue())
		{
			assert value != null;
			return trueClosure.closure(value);
		}
		else
		{
			return falseClosure.closure();
		}
	}

	public boolean equals(final Object o)
	{
		if (this == o)
		{
			return true;
		}
		if (o == null || getClass() != o.getClass())
		{
			return false;
		}

		final CacheItem that = (CacheItem) o;

		return cache.equals(that.cache) && key.equals(that.key);
	}

	public int hashCode()
	{
		int result;
		result = cache.hashCode();
		result = 31 * result + key.hashCode();
		return result;
	}
/* Not quite static - needs to be a live check, really in case of weak references or deletes.
	public boolean isCached()
	{
		return true;
	}

	public final boolean isUncached()
	{
		return !isCached();
	}*/
}
